home *** CD-ROM | disk | FTP | other *** search
/ Apple WWDC 1996 / WWDC96_1996 (CD).toast / Technology Materials / Newton Platform Info / Newton 2.0 Sample Code / Desktop Connectivity / SoupDrink-2 / SoupDrink-Newton-1 / SoupDrink.text < prev    next >
Encoding:
Text File  |  1996-01-08  |  19.7 KB  |  652 lines  |  [TEXT/R*ch]

  1. // Text of project SoupDrink written on 1/8/96 at 2:26 PM
  2. // Beginning of file Main.t
  3.  
  4. // Before Script for "DILSample"
  5. // By Rob Langhorne, Christopher Bell, David Fedor, and Jim Schram
  6. // Copyright Apple Computer, Inc. 1995-1996.  All rights reserved.
  7.  
  8.  
  9. DILSample :=
  10.     {
  11.      CreateNBPAddress:
  12.        func(address)
  13.        begin    //* create an ADSP endpoint address option frame and return it
  14.        
  15.            if address then
  16.                if IsFrame(address) then
  17.                    return(TotalClone(address));
  18.                else  // probably a string
  19.                    return {            
  20.                        label:    kCMARouteLabel,
  21.                        type:        'address,
  22.                        opCode:    opSetRequired,
  23.                        data:        {
  24.                                    addressType: kNamedAppleTalkAddress,
  25.                                    addressData: TotalClone(address), 
  26.                                    },
  27.                    }
  28.            else
  29.                return nil
  30.        
  31.        end,
  32.      viewSetupDoneScript:
  33.        func()
  34.        begin
  35.            // make a RAM copy here so we can modify it & access methods in base App
  36.            ep := {    _proto:        baseEndpoint,
  37.                    _parent:    self,
  38.                    appBase:    self,    };
  39.        end,
  40.      GoDrink:
  41.        func(s)
  42.        begin
  43.            local theStr;
  44.            local theNames;
  45.        
  46.            if (BeginsWith(s, "OK")) then begin
  47.                local theEntry := theCursor:Entry();
  48.        
  49.                if theEntry then
  50.                    begin
  51.                        ep:Output("ENTR", frameOptions);    // tell the server we are sending a soup entry
  52.                        ep:FlushOutput();
  53.        
  54.                        if spitMode then begin              // if we are only sending strings because of ADSP OutputFrame bug
  55.                            theStr := "";
  56.                            if isframe(theEntry.Name) then begin
  57.                                if theEntry.name.honorific exists then
  58.                                    theStr := theEntry.name.honorific & " ";
  59.                                if theEntry.name.first exists then
  60.                                    theStr := theEntry.name.first & " ";
  61.                                if theEntry.name.last exists then
  62.                                    theStr := theStr & theEntry.name.last;
  63.                            end;
  64.                            TrimString(theStr);                // remove any extra white space
  65.                            if StrLen(theStr) = 0 then 
  66.                                thestr:="<no name in this record>";
  67.        
  68.                            // allocate 4-byte object
  69.                            local lengthObj := setclass(setlength(clone(""), 4), 'binary);
  70.                            StuffLong(lengthObj, 0, strlen(theStr)); // turn the string length into a 4byte int
  71.                            
  72.                            // output string length as 4 byte. as normal int, output sends only a one-byte int
  73.                            ep:Output(lengthObj, frameOptions);
  74.                            ep:Output(theStr, frameOptions);
  75.                        end else                            // we're sending frames; just send the whole thing over.
  76.                            ep:OutputFrame(theEntry, frameOptions);
  77.        
  78.                        ep:FlushOutput();
  79.                
  80.                        if theEntry then
  81.                            frameCount := frameCount + 1;        //    increment counter
  82.                  
  83.                        :PutDataInStatusArea("Frame Sent: " && NumberStr(frameCount));
  84.                                
  85.                        theEntry := theCursor:Next();
  86.                        ep:SetInputSpec(ep.doEntry);    // this shouldn't be necessary but works around a Newton 2.0 bug-ette
  87.                    end;
  88.                else begin
  89.                    ep:Output("END ", frameOptions); // tell the server we are done
  90.                    ep:FlushOutput(); 
  91.                    :PutDataInStatusArea("All Frames Sent");
  92.                    :DisposeConnection();
  93.                end;
  94.            end else begin
  95.                :PutDataInStatusArea("Unknown Command Received...");
  96.                ep:SetInputSpec(ep.doInput);
  97.            end;
  98.        end,
  99.      ep: nil,
  100.      InitConnection:
  101.        func(passive)
  102.        begin
  103.            local address := nil;
  104.        
  105.            :PutDataInStatusArea("Trying to connect...");
  106.            RefreshViews();
  107.            
  108.            ep.configOptions := configOptions.(endpointType);
  109.            
  110.            local err;
  111.            try
  112.                err := ep:Instantiate(ep, nil)
  113.            onexception |evt.ex| do
  114.                err := -666;
  115.            
  116.            if err then
  117.                begin
  118.                    :Notify(kNotifyAlert, EnsureInternal("SoupDrink"), EnsureInternal("Sorry, can't create an endpoint!"));
  119.                    :PutDataInStatusArea("Unable to initialize...");
  120.                    if endpointType = 'adsp then CloseAppleTalk();
  121.                    return;
  122.                end;
  123.        
  124.            if endpointType = 'adsp then
  125.                address := NBPaddress;        // this was filled in by the chooser
  126.        
  127.            :MakeConnection(passive, address);
  128.        end,
  129.      theSoup: nil,
  130.      viewQuitScript:
  131.        func()
  132.        begin
  133.        RemovePowerOffHandler(self);
  134.        end,
  135.      frameCount: 0,
  136.      SetEndpointType:
  137.        func(t)
  138.        begin
  139.            endpointType := t;
  140.        end,
  141.      compileTime:
  142.        time(); // this will evaluate to the compile time
  143.        ,
  144.      NetworkChooserDone:
  145.        func(selection, zone)
  146.        begin
  147.            if selection then
  148.                begin
  149.                    local passive := nil;
  150.                    local address := selection & ":" & vNBPtype & "@" & if zone then zone else "*";    //* convert selection into an NBP address
  151.                    NBPaddress := :CreateNBPAddress(address);
  152.                    :PutDataInStatusArea("About to Connect...");
  153.                    :InitConnection(passive);
  154.                end;
  155.            else
  156.                :PutDataInStatusArea("Ready to connect...(cancelled chooser)");
  157.        end,
  158.      Connected: nil,
  159.      baseEndpoint:
  160.        {
  161.            _proto: protoEndpoint,
  162.        
  163.            configOptions:  nil,  // overridden by each endpoint type
  164.        
  165.            appBase: nil,
  166.        
  167.            exceptionHandler: func(exception)
  168.                begin
  169.                    if (exception.data <> -16005 and exception.data <> -38001) then
  170.                        GetRoot():Notify(kNotifyAlert, EnsureInternal("SoupDrink"), EnsureInternal("Exception: " & exception.data));
  171.                    true;
  172.                end,
  173.        
  174.           doInput:
  175.              {
  176.                inputForm:        'string,
  177.                endCharacter:    unicodeEOT,
  178.                discardAfter:    256,
  179.        
  180.                InputScript : func(endpoint, s)
  181.                begin
  182.                    endpoint:PutDataInStatusArea("Command Received...");
  183.                    endpoint.appBase:StartDrink(s);
  184.                end,
  185.              },
  186.        
  187.           doEntry:
  188.              {
  189.                inputForm:        'string,
  190.                endCharacter:    unicodeEOT,
  191.                discardAfter:    256,
  192.        
  193.                InputScript : func(endpoint, s)
  194.                begin
  195.                    endpoint.appBase:GoDrink(s);
  196.                end,
  197.              },
  198.        
  199.            doFrame:
  200.                {
  201.                  inputForm: 'frame ,
  202.        
  203.                  InputScript: func(endpoint, f)
  204.                     begin
  205.                        endpoint:output("OK",endpoint.appBase.frameOptions);        // tell the other side we got it; they can disconnect.
  206.                        endpoint.appBase:AddName(f);
  207.                    end,
  208.                },
  209.        },
  210.      PutDataInStatusArea:
  211.        func(s)
  212.        begin
  213.           SetValue(StatusArea, 'text, s);
  214.           AddArraySlot(StatusMessages, s);        // keep the messages around for debugging purposes
  215.        end,
  216.      configOptions:
  217.        {        serial:   [        // serial tool
  218.                     { label:     "aser", type: 'service, opCode: opSetRequired },
  219.        
  220.                     { label: kCMOSerialIOParms, type: 'option, opCode: opSetNegotiate,
  221.                          data: { bps: k38400bps, dataBits: k8DataBits, stopBits: k1StopBits, parity: kNoParity } },
  222.                ],
  223.                mnp:   [         //    MNP
  224.                     { label:     kCMSMNPID, type: 'service, opCode: opSetRequired },
  225.        
  226.                     { label: kCMOSerialIOParms, type: 'option, opCode: opSetNegotiate,
  227.                          data:
  228.                          { bps: k38400bps, dataBits: k8DataBits, stopBits: k1StopBits, parity: kNoParity } 
  229.                             },
  230.                     { label: kCMOMNPCompression, type: 'option, opCode: opSetRequired, data: kMNPCompressionV42bis},
  231.                     { label: kCMOMNPAllocate, type: 'option, opCode: opSetRequired, data: kMNPDoAllocate},
  232.                     { label: kCMOMNPDataRate, type: 'option, opCode: opSetRequired, data: k38400bps},
  233.                ],
  234.                modemMNP:   [        // modem mnp tool
  235.                     { label:     kCMSModemID, type: 'service, opCode: opSetRequired },
  236.                     { label: kCMOModemECType, type: 'option, opCode: opSetRequired,
  237.                          data: kModemECProtocolMNP },
  238.                     { label: kCMOMNPAllocate, type: 'option, opCode: opSetRequired,
  239.                          data: kMNPDoAllocate },
  240.                     { label: kCMOMNPCompression, type: 'option, opCode: opSetRequired,
  241.                          data: kMNPCompressionMNP5 },
  242.                ],
  243.                adsp:   [        // adsp (AppleTalk) tool
  244.                   {   label:   kCMSAppleTalkID,
  245.                       type:    'service,
  246.                       opCode:  opSetRequired },
  247.                
  248.                   {   label:   kCMSAppleTalkID,
  249.                       type:    'option,
  250.                       opCode:  opSetRequired,
  251.                       data:    kCMOAppleTalkADSP },
  252.                   
  253.                   {   label:   kCMOEndpointName,
  254.                       type:    'option,
  255.                       opCode:  opSetRequired,
  256.                       data:    kADSPEndpoint },
  257.                   ],
  258.                };,
  259.      StartDrink:
  260.        func(s)
  261.        begin
  262.            local theStr, theNames; 
  263.        
  264.            frameCount := 0;
  265.            if (BeginsWith(s, "DRNK")) then 
  266.                    begin
  267.        
  268.                    // Because of problems with ADSP and OutputFrame in unpatched 1.x Newton devices, we 
  269.                    // sense when we cannot use ADSP frames.  This bug was fixed in the December '95 patches.
  270.                    if (endpointType = 'adsp) and (SendADSPFrames.viewValue = nil) then begin
  271.                        :PutDataInStatusArea("ADSP OutputFrame not supported..." && soupName);
  272.                        spitMode := true;
  273.                        ep:Output("SPIT", frameOptions); // tell the server we can only spit, not drink
  274.                    end else begin
  275.                        spitMode := nil;
  276.                        ep:Output("DRNK", frameOptions); // tell the server we can do a drink
  277.                    end;
  278.        
  279.                    ep:FlushOutput();
  280.        
  281.                    local soupName := SubStr(s, 4, strLen(s) - 5 );
  282.        
  283.                    theSoup := GetUnionSoup(soupName);
  284.        
  285.                    if NOT theSoup then
  286.                        :PutDataInStatusArea("Unknown Soup Requested..." && soupName);
  287.                    else begin
  288.                        :PutDataInStatusArea("Open Soup: " && soupName);
  289.                         local queryType := {type: 'index};
  290.            
  291.                           theCursor := Query(theSoup, queryType);
  292.                        local theEntry := theCursor:Entry();
  293.        
  294.                        ep:Output("ENTR", frameOptions); // tell the server we are sending a soup entry
  295.                        ep:FlushOutput();
  296.        
  297.                        if spitMode then begin     /* if we are only sending strings */
  298.                            theStr := "";
  299.                            if isframe(theEntry.Name) then begin
  300.                                if theEntry.name.honorific exists then
  301.                                    theStr := theEntry.name.honorific & " ";
  302.                                if theEntry.name.first exists then
  303.                                    theStr := theEntry.name.first & " ";
  304.                                if theEntry.name.last exists then
  305.                                    theStr := theStr & theEntry.name.last;
  306.                            end;
  307.                            TrimString(theStr);                // remove any extra white space
  308.                            if StrLen(theStr) = 0 then 
  309.                                thestr:="<no name in this record>";
  310.        
  311.                            // allocate 4-byte object to hold the length
  312.                            local lengthObj := setclass(setlength(clone(""), 4), 'binary);
  313.                            StuffLong(lengthObj, 0, strlen(theStr)); // turn the string length into a 4byte int
  314.                            
  315.                            // output string length as 4 byte int; output sends only a one-byte int
  316.                            ep:Output(lengthObj, frameOptions);
  317.                            ep:Output(theStr, frameOptions);
  318.                            end;
  319.                        else                    /* if we are sending frames */
  320.                            ep:OutputFrame(theEntry, frameOptions);
  321.        
  322.                        ep:FlushOutput();
  323.        
  324.                        frameCount := frameCount + 1;        //    increment counter
  325.                           :PutDataInStatusArea("Frame Sent: " && NumberStr(frameCount));
  326.                        
  327.                        theEntry := theCursor:Next();
  328.                        ep:SetInputSpec(ep.doEntry);
  329.                    end;
  330.                end;
  331.            else if (BeginsWith(s, "NAME")) then begin
  332.                        theSoup := GetUnionSoup("Names");
  333.                       if NOT theSoup then begin
  334.                            :PutDataInStatusArea("Names soup doesn't exist (?!?)...");
  335.                            refreshviews();
  336.                            :DisposeConnection();
  337.                        end else begin
  338.                            :PutDataInStatusArea("Open Names Soup");
  339.                            refreshviews();
  340.                            ep:SetInputSpec(ep.doFrame);
  341.                        end;
  342.                end;
  343.            else if (BeginsWith(s, "HELO")) then begin    // this is used by the DILette sample... just respond "Hello" and disconnect.
  344.                    ep:Output("Hello", frameOptions);
  345.                    ep:FlushOutput();
  346.                    GetRoot():Notify(knotifyAlert, clone("SoupDrink"), clone("Hello, DILette... pleased to meet you!"));
  347.                    :DisposeConnection();
  348.                end;
  349.            else
  350.                  :PutDataInStatusArea("Unknown Command:" && s);
  351.        end,
  352.      viewBounds: {top: 0, left: 0, right: 240, bottom: 330},
  353.      endpointType: nil,
  354.      _proto: @157,
  355.      DisposeConnection:
  356.        func()
  357.        begin
  358.            :PutDataInStatusArea("Disconnecting...");
  359.            :ReleaseConnection();
  360.            :PutDataInStatusArea("Ready to connect...");
  361.            ConnectButton:Show();
  362.        end,
  363.      theCursor: nil,
  364.      MakeConnection:
  365.        func(passive, address)
  366.        begin
  367.            local err := ep:Connect(address, nil);
  368.            if err then
  369.                begin
  370.                    :PutDataInStatusArea("Couldn't connect..." & NumberStr(err));
  371.                    ep:Dispose();
  372.                    if endpointType = 'adsp then CloseAppleTalk();
  373.                    return;
  374.                end;
  375.            
  376.            connected := true;
  377.            :PutDataInStatusArea("Connected!!");
  378.            ConnectButton:Hide();
  379.            
  380.            if endpointType = 'ir  or endpointType = 'adsp then
  381.                frameOptions := kFrame;
  382.            else
  383.                frameOptions := nil;
  384.            
  385.            :PutDataInStatusArea("Waiting for data...");
  386.            ep:SetInputSpec(ep.doInput);
  387.        end,
  388.      MLookup:
  389.        func()
  390.        begin
  391.            if not connected then    // use netchooser to find type: vNBPtype
  392.                begin
  393.                    GetRoot().NetChooser:OpenNetChooser(nil,
  394.                                                        "=:" & vNBPtype & "@",
  395.                                                        nil,
  396.                                                        self,
  397.                                                        "Pick this one",
  398.                                                        "SoupDrink Apps",
  399.                                                        "SoupDrinks");
  400.                end;
  401.        end,
  402.      title: "SoupDrink",
  403.      powerOffScript:
  404.        func(what)
  405.        begin
  406.            if self.connected then return nil else true;
  407.        end,
  408.      NBPaddress: nil,
  409.      vNBPtype: "SoupDrink",
  410.      viewSetupFormScript:
  411.        func()
  412.        begin
  413.            local b := GetAppParams();
  414.            constant kMaxWidth := 240;
  415.            constant kMaxHeight := 336;
  416.            
  417.            viewBounds := RelBounds(b.appAreaLeft, b.appAreaTop,
  418.                    Min(b.appAreaWidth, kMaxWidth),
  419.                    Min(b.appAreaHeight, kMaxHeight) );
  420.            
  421.            AddPowerOffHandler(self);
  422.            self.statusMessages := [];
  423.        end,
  424.      ReleaseConnection:
  425.        func()
  426.        begin
  427.            if connected then
  428.              begin
  429.                ep.nextInputSpec := nil;             // no more input
  430.                ep:SetInputSpec(nil);                // kill the current inputSpec
  431.                ep:Abort();                          // asynchronously abort any pending I/O
  432.        
  433.                // must use a delayed action to dispose
  434.                AddDelayedAction(    func(ep, isAppleTalk)
  435.                                    begin
  436.                                        ep:Disconnect();
  437.                                        ep:Dispose();
  438.                                        if isAppleTalk then CloseAppleTalk();
  439.                                        ep.appBase.connected := nil;
  440.                                    end,
  441.                                    [ ep, endpointType = 'adsp ], 2500);
  442.              end;
  443.        end,
  444.      AddName:
  445.        func(f)
  446.        begin    
  447.            theSoup:AddToDefaultStore(f);
  448.            BroadcastSoupChange("Names");
  449.            
  450.            :PutDataInStatusArea("Frame added...");
  451.           :DisposeConnection();
  452.        end,
  453.      spitMode: nil,
  454.      debug: "DILSample",
  455.      frameOptions: nil
  456.     };
  457.  
  458. StatusArea :=
  459.     {text: "",
  460.      viewBounds: {left: 10, top: 75, right: 232, bottom: 113},
  461.      viewJustify: 0,
  462.      viewFormat: 67436880,
  463.      viewFont: simpleFont18,
  464.      debug: "StatusArea",
  465.      _proto: @218
  466.     };
  467. AddStepForm(DILSample, StatusArea);
  468. StepDeclare(DILSample, StatusArea, 'StatusArea);
  469.  
  470.  
  471.  
  472. ConnectButton :=
  473.     {text: "Connect",
  474.      buttonClickScript:
  475.        func()
  476.        begin
  477.            if not connected then
  478.                begin
  479.                    statusMessages := [];        // clear out our array of status messages
  480.                    :SetEndpointType(ConnectionType.clusterValue);
  481.                    if endpointType = 'adsp then
  482.                        :MLookup();                            // bring up the chooser; it will call initconnection
  483.                    else
  484.                        :InitConnection(nil);               // active mode
  485.                end;
  486.            else
  487.                :Notify(kNotifyAlert, EnsureInternal("SoupDrink"), EnsureInternal("Already connected!"));
  488.        end,
  489.      viewBounds: {left: 12, top: 148, right: 75, bottom: 165},
  490.      viewSetupDoneScript:
  491.        func()
  492.        begin
  493.           :PutDataInStatusArea("Ready to Connect...");
  494.        end,
  495.      debug: "ConnectButton",
  496.      _proto: @226
  497.     };
  498. AddStepForm(DILSample, ConnectButton);
  499. StepDeclare(DILSample, ConnectButton, 'ConnectButton);
  500.  
  501.  
  502.  
  503. DisconnectButton :=
  504.     {text: "Disconnect",
  505.      buttonClickScript:
  506.        func()
  507.        begin
  508.            if connected then
  509.                :DisposeConnection();
  510.             else
  511.                :PutDataInStatusArea("Ready to connect...");
  512.        end,
  513.      viewBounds: {left: 159, top: 149, right: 230, bottom: 166},
  514.      viewQuitScript:
  515.        func()
  516.        begin
  517.           :ReleaseConnection();
  518.        end,
  519.      debug: "DisconnectButton",
  520.      _proto: @226
  521.     };
  522. AddStepForm(DILSample, DisconnectButton);
  523. StepDeclare(DILSample, DisconnectButton, 'DisconnectButton);
  524.  
  525.  
  526.  
  527. ConnectionType :=
  528.     {viewBounds: {left: 39, top: 191, right: 213, bottom: 254},
  529.      viewSetupDoneScript:
  530.        func()
  531.        begin
  532.            :SetClusterValue('mnp);
  533.        end,
  534.      debug: "ConnectionType",
  535.      _proto: @203
  536.     };
  537. AddStepForm(DILSample, ConnectionType);
  538. StepDeclare(DILSample, ConnectionType, 'ConnectionType);
  539.  
  540. serial :=
  541.     {buttonValue: 'serial,
  542.      viewBounds: {left: 7, top: 5, right: 155, bottom: 23},
  543.      text: "Serial",
  544.      debug: "serial",
  545.      _proto: @202
  546.     };
  547. AddStepForm(ConnectionType, serial);
  548.  
  549.  
  550.  
  551. mnp :=
  552.     {buttonValue: 'mnp,
  553.      viewBounds: {left: 7, top: 25, right: 155, bottom: 43},
  554.      text: "Serial with MNP",
  555.      debug: "mnp",
  556.      _proto: @202
  557.     };
  558. AddStepForm(ConnectionType, mnp);
  559.  
  560.  
  561.  
  562. adsp :=
  563.     {buttonValue: 'adsp,
  564.      viewBounds: {left: 7, top: 45, right: 155, bottom: 63},
  565.      text: "AppleTalk (ADSP)",
  566.      debug: "adsp",
  567.      _proto: @202
  568.     };
  569. AddStepForm(ConnectionType, adsp);
  570.  
  571.  
  572.  
  573.  
  574.  
  575. SendADSPFrames :=
  576.     {text: "Send frames over ADSP",
  577.      viewBounds: {left: 60, top: 256, right: 233, bottom: 273},
  578.      viewValue: nil,
  579.      debug: "SendADSPFrames",
  580.      _proto: @164
  581.     };
  582. AddStepForm(DILSample, SendADSPFrames);
  583. StepDeclare(DILSample, SendADSPFrames, 'SendADSPFrames);
  584.  
  585.  
  586.  
  587. _view000 :=
  588.     {
  589.      text:
  590.        "(Only enable this with Newton 2.0
  591.          or a 1.x unit with the Dec '95 patch)",
  592.      viewBounds: {left: 71, top: 272, right: 236, bottom: 306},
  593.      viewFont: ROM_fontSystem9,
  594.      viewJustify: 0,
  595.      _proto: @218
  596.     };
  597. AddStepForm(DILSample, _view000);
  598.  
  599.  
  600.  
  601. compileTime :=
  602.     {viewFlags: 513,
  603.      viewFormat: nil,
  604.      viewBounds: {left: 156, top: 0, right: 239, bottom: 22},
  605.      viewClickScript:
  606.        func(unit)
  607.        begin
  608.        if :trackhilite(unit) then
  609.            begin
  610.            self.showcompiletime := not self.showcompiletime;
  611.            :dirty();
  612.            end;
  613.        
  614.        :hilite(nil);
  615.        end,
  616.      viewDrawScript:
  617.        func()
  618.        begin
  619.        if self.showcompiletime then
  620.            begin
  621.            local box := :localbox();
  622.            :DrawShape(MakeText(datentime(compiletime),box.left,box.top,box.right,box.bottom), {font:viewfont}); 
  623.            end;
  624.        end,
  625.      viewFont: {family: 'geneva, size: 9, face: 0},
  626.      debug: "compileTime",
  627.      viewClass: 74
  628.     };
  629. AddStepForm(DILSample, compileTime);
  630.  
  631.  
  632.  
  633. status :=
  634.     {text: "Status",
  635.      viewBounds: {left: 18, top: 67, right: 60, bottom: 82},
  636.      viewTransferMode: 0
  637.      ,
  638.      debug: "status",
  639.      _proto: @218
  640.     };
  641. AddStepForm(DILSample, status);
  642. StepDeclare(DILSample, status, 'status);
  643.  
  644.  
  645.  
  646.  
  647. constant |layout_Main.t| := DILSample;
  648. // End of file Main.t
  649.  
  650.  
  651.  
  652.